﻿
package extremefx.ui {
	import extremefx.IDisposable;
	
	import flash.geom.Point;
	import flash.ui.Keyboard;	

	/**
	 * @author Marcelo Volmaro
	 */
	internal final class TabGroup implements IDisposable {
		private static var _lastFocused:TabGroup;
		private static var _doInit:Boolean = _init();
		
		private var _elements:Vector.<TabInfo>;
		private var _components:Vector.<Component>;
		private var _index:int;
		private var _dirty:Boolean;
		private var _focused:Boolean;
		private var _active:Boolean;
		private var _container:Container;
		
		public var tabChildren:Boolean;
		public var closedGroup:Boolean;
		public var tabSortScheme:uint = TabScheme.DOWN_FIRST;
		private var _disposing:Boolean;

		public function TabGroup(pContainer:Container) {
			tabChildren = true;
			_index = -1;
			_elements = new Vector.<TabInfo>();
			_components = new Vector.<Component>();
			_container = pContainer;
			if (_lastFocused) {
				_active = false;
				
			} else {
				_lastFocused = this;
				_active = true;
			}
		}
		
		private static function _init():Boolean {
			UIStage.STAGE.tabChildren = UIStage.STAGE.stageFocusRect = false;
			KeyHandler.addKeyHandler(Keyboard.TAB, _tabHandler);
			return true;
			_doInit;
		}
		
		private static function _tabHandler(pSKStatus:uint):void {
			var c:UIComponent = _lastFocused._focusComponent(pSKStatus & 0x01 ? -1 : 1);
			if (c) {
				c.tabFocus = true;
			}
		}
		
		private function get parent():TabGroup {
			return _container.parent ? _container.parent._containerTabGroup : null; 
		}
		
		public function hasFocus():Boolean{
			return _focused;
		}
		
		public function isActive():Boolean{
			return _active;
		}
		
		public function focus(pComponent:Component):void {
			var lf:TabGroup = _lastFocused;
			if (lf){
				lf._active = false;
				
				do {
					lf._focused = false;
				} while (lf = lf.parent);
			}
			
			lf = this;
			do {
				lf._focused = true;
			} while (lf = lf.parent);
			
			lf = _lastFocused;
			do {
				if (!lf._focused) lf._index = -1;
			} while (lf = lf.parent);
			
			_lastFocused = this;
			_active = true;
			_updateIndexes();
			_index = _components.indexOf(pComponent);
		}

		private function _focusComponent(pDir:int, pIndex:int = -1):UIComponent{
			if (!tabChildren && parent) return parent._focusComponent(pDir);
			
			_updateIndexes();
			var e:Vector.<Component> = _components;
			
			if (!e.length) return null;
			
			var l:uint = e.length - 1;
			var tg:TabGroup;
			var c:Component;
		
			if (pIndex == -1) pIndex = _index;
					
			if (-1 < pIndex && pIndex <= l){
				c = e[pIndex];
				if (c is UIComponent){
				var ui:UIComponent = c as UIComponent;
					if (!ui.tabFocus && ui.isFocusable() && ui.isTabEnabled()) {
						return ui;
					}
				}
			}
			
			pIndex += pDir;
			
			if (l < pIndex || pIndex < 0) {//out of range...
				if (closedGroup){
					pIndex = pIndex < 0 ? l : 0;
					
				} else {//up one level
					tg = parent;
					if (tg){
						return tg._focusComponent(pDir);
						
					} else {
						pIndex = pIndex < 0 ? l : 0;
					}
				}
			}
			
			c = e[pIndex];
			if (c is Container){//dig down one level
				tg = (c as Container)._containerTabGroup;
				_index = pIndex;
				return tg._focusComponent(pDir);//skip group if no tab children
					
			} else if ((c as UIComponent).isTabEnabled()) {
				_index = pIndex;
				return (c as UIComponent);
			}

			return _focusComponent(pDir, pIndex);
		}

		public function addComponent(pComponent:Component, pIndex:int = int.MAX_VALUE):void{
			if (!(pComponent is Component)) return;
			if (pIndex == -1) {
				removeComponent(pComponent);
				return;
			}
			
			_elements.push(new TabInfo(pComponent, pIndex));
			_dirty = true;
		}
		
		public function removeComponent(pComponent:Component):void {
			var found:int = -1;
			
			_elements.forEach(function(pTabInfo:TabInfo, pIndex:int, pArray:Vector.<TabInfo>):void{
				if (pTabInfo.component == pComponent) {
					found = pIndex;
				}
			});
			
			if (found != -1){
				_elements.splice(found, 1);
			}
			
			_dirty = true;
		}
		
		private function _updateIndexes():void {
			if (!_dirty) return;
			
			_dirty = false;
			
			_elements.sort(_compare);
			var b:Vector.<Component> = new Vector.<Component>();
			for each (var i:TabInfo in _elements) {
				b.push(i.component);
			}
			
			_components = b;
		}
		
		private function _compare(control1:TabInfo, control2:TabInfo):int {
			if (control1 == null || control2 == null) return 0;
			
			if (control1.index != int.MAX_VALUE && control2.index == int.MAX_VALUE) return -1;
			if (control1.index == int.MAX_VALUE && control2.index != int.MAX_VALUE) return 1;
			
			if (control1.index != int.MAX_VALUE && control2.index != int.MAX_VALUE) return control1.index - control2.index;
			
			if (tabSortScheme == TabScheme.NONE) return 0;
			
			var pos1:Point = control1.component.position;
			var pos2:Point = control2.component.position;

			if (tabSortScheme == TabScheme.ACROSS_FIRST){
				// The primary direction to sort is the y direction (using the Top property).
				// If two controls have the same y coordination, then we sort them by their x's.
				if (pos1.y == pos2.y){
					return pos1.x - pos2.x;
				}
				
				return pos1.y - pos2.y;
				
			} else {
				// The primary direction to sort is the x direction (using the Left property).
				// If two controls have the same x coordination, then we sort them by their y's.
				
				if (pos1.x == pos2.x){
					return pos1.y - pos2.y;
				}
				
				return pos1.x - pos2.x;
			}
		}
		
		public function dispose():void {
			if (_disposing) return;
			_disposing = true;
			
			_elements = null;
			_components = null;
		}
	}
}

import extremefx.ui.Component;

final class TabInfo {
	public var component:Component;
	public var index:int;
	
	public function TabInfo(pComponent:Component, pIndex:int) {
		component = pComponent;
		index = pIndex;
	}
}
